/*
  环境监测系统
  整合了光敏电阻、温湿度传感器、人体移动传感器、MQ-135传感器和红外信号学习与重发功能。
  增加了WiFi一键配网功能，配置信息存储在EEPROM中。
*/

#include <DHT.h>      // 引入 DHT 库
#include <IRremote.hpp>  // 引入 IRremote 库
#include <WiFi.h>      // 引入 WiFi 库
#include <WiFiManager.h> // 引入 WiFiManager 库
#include <Preferences.h> // 引入 Preferences 库用于存储配置
#include <EEPROM.h>    // 引入 EEPROM 库用于红外信号存储
#include <PubSubClient.h> // 引入 MQTT 库
#include <ArduinoJson.h> // 引入 JSON 库
#include <Wire.h>      // 引入 Wire 库
#include <SPI.h>      // 引入 SPI 库
#include <Adafruit_Sensor.h> // 引入 Adafruit 传感器库
#include <Adafruit_BMP280.h> // 引入 BMP280 库


// 定义红外信号结构体
struct IrSignal {
  decode_type_t protocol = UNKNOWN;
  uint16_t address = 0;
  uint8_t command = 0;
  uint32_t rawData = 0;
  uint16_t rawBuf[500]; // 增加RAW数据存储长度从300到500
  uint16_t rawLen = 0;  // RAW数据长度
};

// 函数前向声明
void sendStoredSignal(int index);
void printSignalInfo(IrSignal* signal);

// 定义引脚
const int LDR_AO_PIN = 36; // 光敏电阻模拟输出连接到模拟引脚A0
const int DHTPIN = 33;         // 连接 DHT11 数据引脚的数字引脚
#define DHTTYPE DHT11    // 定义使用的 DHT 类型是 DHT11
const int pirPin = 23;  // 连接SR602的OUT到GPIO23

// 定义MQ-135传感器的引脚
const int mq135AnalogPin = 39;  // MQ-135传感器的模拟输出引脚

// 定义红外接收和发送引脚
#define IR_RX_PIN 32
#define IR_TX_PIN 5
#define RAWTICK 50  // 定义原始数据计时

// 定义按钮和LED引脚
const int BUTTON_PIN_1 = 16; // 第一个按钮连接到GPIO 16
const int BUTTON_PIN_2 = 17; // 第二个按钮连接到GPIO 17
const int BUTTON_PIN_3 = 18; // 第三个按钮连接到GPIO 18 - 用于触发配网
const int LED_PIN = 19;    // LED连接到GPIO 19

// 存储按钮状态的变量
int buttonState1 = HIGH; // 默认为高电平（未按下）
int buttonState2 = HIGH;
int buttonState3 = HIGH;

// 存储上一次按钮状态的变量（用于检测变化）
int lastButtonState1 = HIGH;
int lastButtonState2 = HIGH;
int lastButtonState3 = HIGH;

// LED状态变量
int ledState = LOW; // 默认LED关闭
int lightBrightness = 200; // 默认亮度为最大值 (0-255)

// 传感器读取时间间隔相关变量
unsigned long currentTime = 0;
unsigned long lastReadTime = 0;
long interval = 600000; // 读取间隔，例如600000毫秒 (十分钟)

// 创建一个 DHT 对象
DHT dht(DHTPIN, DHTTYPE);

// 创建 BMP280 对象
Adafruit_BMP280 bme; // I2C接口

// 多个红外信号存储
#define MAX_IR_SIGNALS 5 // 最多存储5个红外信号
IrSignal irSignals[MAX_IR_SIGNALS]; // 红外信号数组
IrSignal lastSignal; // 最近一次存储的信号
int currentIrSignalIndex = 0; // 当前录入的信号索引
bool isRecordingMode = false; // 是否处于录入模式

// WiFi配置相关
WiFiManager wifiManager;
Preferences preferences; // 用于存储WiFi配置
bool shouldSaveConfig = false; // 标记是否需要保存配置
bool isConfigMode = false; // 是否处于配网模式

// EEPROM存储配置
#define EEPROM_SIZE 4096
#define IR_SIGNALS_START_ADDR 100
#define CURRENT_INDEX_ADDR 50

// 红外信号存储相关
bool shouldSaveIrSignal = false; // 标记是否需要保存红外信号



// 回调函数，当WiFiManager保存配置时调用
void saveConfigCallback() {
  Serial.println("配置需要保存");
  shouldSaveConfig = true;
}

// 初始化WiFi连接
void initWiFi() {
  // 打开命名空间
  preferences.begin("wifi-config", false);
  
  // 检查是否有保存的WiFi配置
  String ssid = preferences.getString("ssid", "");
  String password = preferences.getString("password", "");
  
  if (ssid.length() > 0) {
    Serial.println("找到保存的WiFi配置");
    Serial.print("SSID: ");
    Serial.println(ssid);
    
    // 尝试连接到保存的WiFi
    WiFi.begin(ssid.c_str(), password.c_str());
    
    // 等待连接，最多等待10秒
    int timeout = 10;
    while (WiFi.status() != WL_CONNECTED && timeout > 0) {
      delay(1000);
      Serial.print(".");
      timeout--;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println();
      Serial.println("WiFi连接成功!");
      Serial.print("IP地址: ");
      Serial.println(WiFi.localIP());
    } else {
      Serial.println();
      Serial.println("WiFi连接失败，将启动配网模式");
      startConfigPortal();
    }
  } else {
    Serial.println("没有找到保存的WiFi配置，将启动配网模式");
    startConfigPortal();
  }
}

// 启动配网门户
void startConfigPortal() {
  isConfigMode = true;
  
  // 设置回调函数
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  
  // 设置AP名称
  String apName = "环境监测系统_" + String((uint32_t)ESP.getEfuseMac(), HEX);
  
  // 启动配网门户
  if (wifiManager.startConfigPortal(apName.c_str())) {
    Serial.println("WiFi连接成功!");
    Serial.print("IP地址: ");
    Serial.println(WiFi.localIP());
    
    // 如果需要保存配置
    if (shouldSaveConfig) {
      // 保存WiFi配置到Preferences
      preferences.putString("ssid", WiFi.SSID());
      preferences.putString("password", WiFi.psk());
      Serial.println("WiFi配置已保存");
      shouldSaveConfig = false;
    }
  } else {
    Serial.println("配网失败");
  }
  
  isConfigMode = false;
}

// MQTT配置
const char* mqtt_server = "crychic.link";
const int mqtt_port = 1883;
String deviceId = ""; // 存储设备MAC地址

// MQTT客户端
WiFiClient espClient;
PubSubClient client(espClient);

// MQTT主题
String statusTopic;
String controlTopic;
String irSignalTopic; // 新增红外信号主题

// 上次状态发送时间
unsigned long lastStatusSend = 0;
long statusInterval = 600000; // 状态上报间隔

// 发送所有红外信号信息到MQTT
void sendAllIrSignalsInfo() {
  if (!client.connected()) {
    return;
  }
  
  StaticJsonDocument<2048> doc;
  doc["command"] = "ir_signals_info";
  doc["device_id"] = deviceId;
  doc["current_index"] = currentIrSignalIndex;
  
  JsonArray signals = doc.createNestedArray("signals");
  
  for (int i = 0; i < MAX_IR_SIGNALS; i++) {
    JsonObject signal = signals.createNestedObject();
    signal["index"] = i;
    signal["protocol"] = getProtocolString(irSignals[i].protocol);
    signal["address"] = irSignals[i].address;
    signal["command"] = irSignals[i].command;
    signal["raw_len"] = irSignals[i].rawLen;
    signal["valid"] = (irSignals[i].rawLen > 20);
    
    // 对于有效的RAW协议信号，添加部分原始数据（仅前10个点）
    if (irSignals[i].protocol == UNKNOWN && irSignals[i].rawLen > 20) {
      JsonArray rawSample = signal.createNestedArray("raw_sample");
      for (uint16_t j = 0; j < min(10, (int)irSignals[i].rawLen); j++) {
        rawSample.add(irSignals[i].rawBuf[j]);
      }
    }
  }
  
  String jsonString;
  serializeJson(doc, jsonString);
  client.publish(irSignalTopic.c_str(), jsonString.c_str());
  Serial.println("所有红外信号信息已发布到MQTT");
}

// MQTT回调函数
void callback(char* topic, byte* payload, unsigned int length) {
  String message;
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  
  StaticJsonDocument<2048> doc;
  DeserializationError error = deserializeJson(doc, message);
  
  if (error) {
    Serial.println("解析JSON失败");
    return;
  }
  
  if (doc["command"] == "control") {
    String targetId = doc["params"]["Id"];
    if (targetId == deviceId) {
      String status = doc["params"]["status"];
      String data = doc["params"]["data"];
      
      // 处理LED控制
      if (status == "on") {
        ledState = HIGH;
        analogWrite(LED_PIN, ledState ? lightBrightness : 0);
      } else if (status == "off") {
        ledState = LOW;
        analogWrite(LED_PIN, ledState ? lightBrightness : 0);
      } else if (status == "set_brightness") {
        if (doc["params"].containsKey("brightness")) {
          int newBrightness = doc["params"]["brightness"];
          lightBrightness = constrain(newBrightness, 0, 255);
          analogWrite(LED_PIN, ledState ? lightBrightness : 0);
          Serial.print("亮度已设置为: ");
          Serial.println(lightBrightness);
        }
      } else if (status == "increase_brightness") {
        lightBrightness = min(lightBrightness + 25, 255);
        analogWrite(LED_PIN, ledState ? lightBrightness : 0);
        Serial.print("亮度已增加到: ");
        Serial.println(lightBrightness);
      } else if (status == "decrease_brightness") {
        lightBrightness = max(lightBrightness - 25, 0);
        analogWrite(LED_PIN, ledState ? lightBrightness : 0);
        Serial.print("亮度已减少到: ");
        Serial.println(lightBrightness);
      } else if (status == "toggle_light_ir") {
        // 发送特定红外信号切换灯状态
        int signalIndex = -1; // 默认使用最近一次存储的信号
        
        // 检查是否指定了索引参数
        if (doc["params"].containsKey("index")) {
          signalIndex = doc["params"]["index"];
          if (signalIndex < 0 || signalIndex >= MAX_IR_SIGNALS) {
            Serial.println("无效的信号索引，使用最近一次存储的信号");
            signalIndex = -1;
          }
        }
        
        // 发送指定索引的信号
        if (signalIndex >= 0) {
          Serial.print("发送索引 ");
          Serial.print(signalIndex);
          Serial.println(" 的红外信号");
          sendStoredSignal(signalIndex);
        } else {
          // 兼容旧版本，使用硬编码的特定信号
          String specialSignal = "0,1250,450,1250,400,500,1350,1200,500,1250,450,1300,500,1200,500,400,1350,400,1300,400,1350,400,1350,400";
          
          // 解析RAW数据字符串为uint16_t数组
          int index = 0;
          int commaIndex = specialSignal.indexOf(',');
          uint16_t rawData[500]; // 假设最大500个RAW数据点
          int dataCount = 0;

          while (commaIndex != -1 && dataCount < 200) {
            rawData[dataCount++] = specialSignal.substring(index, commaIndex).toInt();
            index = commaIndex + 1;
            commaIndex = specialSignal.indexOf(',', index);
          }
          // 添加最后一个数据点
          if (index < specialSignal.length() && dataCount < 200) {
            rawData[dataCount++] = specialSignal.substring(index).toInt();
          }

          if (dataCount > 0) {
            // 发送特定信号
            IrSender.sendRaw(rawData, dataCount, 38); // 使用38kHz频率
            Serial.println("发送特定红外信号切换灯状态");
          }
        }
      } else if (status == "restart") {
        // 重启设备
        Serial.println("收到重启命令，设备即将重启...");
        // 闪烁LED指示即将重启
        for (int i = 0; i < 3; i++) {
          analogWrite(LED_PIN, lightBrightness);
          delay(100);
          analogWrite(LED_PIN, 0);
          delay(100);
        }
        delay(500); // 等待串口消息发送完成
        ESP.restart(); // 重启ESP32
      } else if (status == "set_interval") {
        // 设置传感器读取和状态上报间隔
        if (doc["params"].containsKey("interval")) {
          long newInterval = doc["params"]["interval"];
          if (newInterval >= 5000) { // 设置最小间隔为5秒
            interval = newInterval;
            statusInterval = newInterval;
            Serial.print("传感器读取和状态上报间隔已更新为: ");
            Serial.print(interval);
            Serial.println(" 毫秒");
          } else {
            Serial.println("间隔设置无效，最小间隔为5000毫秒");
          }
        }
      } else if (status == "read_now") {
        // 立即读取所有传感器数据并上报
        Serial.println("收到立即读取命令，读取所有传感器数据...");
        readLDR();
        readDHT();
        readMQ135Sensor();
        readBMP280();
        sendStatus();
        Serial.println("传感器数据已上报");
      } else if (status == "send_last_ir") {
        // 发送最近存储的红外信号或指定索引的信号
        int signalIndex = -1; // 默认使用最近一次存储的信号
        
        // 检查是否指定了索引参数
        if (doc["params"].containsKey("index")) {
          signalIndex = doc["params"]["index"];
          if (signalIndex < 0 || signalIndex >= MAX_IR_SIGNALS) {
            Serial.println("无效的信号索引，使用最近一次存储的信号");
            signalIndex = -1;
          }
        }
        
        Serial.println("收到发送红外信号命令");
        sendStoredSignal(signalIndex);
      } else if (status == "delete_ir_signal") {
        // 删除指定索引的红外信号
        int signalIndex = -1;
        
        // 检查是否指定了索引参数
        if (doc["params"].containsKey("index")) {
          signalIndex = doc["params"]["index"];
          if (signalIndex < 0 || signalIndex >= MAX_IR_SIGNALS) {
            Serial.println("无效的信号索引，无法删除");
            return;
          }
          
          Serial.print("收到删除红外信号命令，索引: ");
          Serial.println(signalIndex);
          deleteIrSignalFromFlash(signalIndex);
          Serial.println("红外信号已删除");
        } else {
          Serial.println("删除红外信号命令缺少索引参数");
        }
      } else if (status == "query_ir_signals") {
        // 查询所有红外信号信息
        Serial.println("收到查询红外信号命令，发送所有信号信息");
        sendAllIrSignalsInfo();
      } else if (status == "get_ir_signal_data") {
        // 获取指定索引的红外信号数据
        int signalIndex = -1;
        
        // 检查是否指定了索引参数
        if (doc["params"].containsKey("index")) {
          signalIndex = doc["params"]["index"];
          if (signalIndex < 0 || signalIndex >= MAX_IR_SIGNALS) {
            Serial.println("无效的信号索引，无法获取数据");
            return;
          }
          
          Serial.print("收到获取红外信号数据命令，索引: ");
          Serial.println(signalIndex);
          sendIrSignalInfoToMQTT(signalIndex);
        } else {
          Serial.println("获取红外信号数据命令缺少索引参数");
        }
      } else if (status == "send_ir_signal_from_flash") {
        // 从Flash发送指定索引的红外信号
        int signalIndex = -1;
        
        // 检查是否指定了索引参数
        if (doc["params"].containsKey("index")) {
          signalIndex = doc["params"]["index"];
          if (signalIndex < 0 || signalIndex >= MAX_IR_SIGNALS) {
            Serial.println("无效的信号索引，无法发送");
            return;
          }
          
          Serial.print("收到从Flash发送红外信号命令，索引: ");
          Serial.println(signalIndex);
          
          // 检查信号是否有效
          if (irSignals[signalIndex].rawLen <= 20) {
            Serial.println("信号无效或太短，无法发送");
            return;
          }
          
          // 发送信号
          sendStoredSignal(signalIndex);
          Serial.println("已从Flash发送红外信号");
        } else {
          Serial.println("从Flash发送红外信号命令缺少索引参数");
        }
      }
    }
  } else if (doc["command"] == "ir_send") {
    String protocol = doc["params"]["protocol"];
    long value = doc["params"]["value"];
    int bits = doc["params"]["bits"];
    
    // 根据协议类型调用相应的发送函数
    if (protocol == "NEC") {
      IrSender.sendNEC(value, bits);
      Serial.println("发送NEC红外信号");
    } else if (protocol == "SONY") {
      IrSender.sendSony(value, bits);
      Serial.println("发送SONY红外信号");
    } else if (protocol == "RC5") {
      IrSender.sendRC5(value, bits);
      Serial.println("发送RC5红外信号");
    } else if (protocol == "RC6") {
      IrSender.sendRC6((uint32_t)value, bits);
      Serial.println("发送RC6红外信号");
    } else if (protocol == "RAW") {
      // RAW数据需要特殊处理，解析逗号分隔的字符串
      String rawDataString = doc["params"]["data"];
      int frequency = doc["params"]["frequency"] == 0 ? 38 : doc["params"]["frequency"]; // 默认38kHz
      
      // 解析RAW数据字符串为uint16_t数组
      // 假设RAW数据字符串格式为 "val1,val2,val3,..."
      int index = 0;
      int commaIndex = rawDataString.indexOf(',');
      uint16_t rawData[500]; // 假设最大500个RAW数据点
      int dataCount = 0;

      while (commaIndex != -1 && dataCount < 200) {
        rawData[dataCount++] = rawDataString.substring(index, commaIndex).toInt();
        index = commaIndex + 1;
        commaIndex = rawDataString.indexOf(',', index);
      }
      // 添加最后一个数据点
      if (index < rawDataString.length() && dataCount < 200) {
         rawData[dataCount++] = rawDataString.substring(index).toInt();
      }

      if (dataCount > 0) {
        bool hasInvalidDuration = false;
        for (int i = 0; i < dataCount; i++) {
          if (rawData[i] == 0) {
            hasInvalidDuration = true;
            Serial.println("警告: RAW数据包含0微秒的持续时间，这可能导致发送失败。");
            break;
          }
        }
                  if (!hasInvalidDuration) {
            IrSender.sendRaw(rawData, dataCount, frequency);
            Serial.print("发送RAW红外信号，数据点数: ");
            Serial.println(dataCount);
          
          //检查是否是切换灯状态的特定信号
          if (checkSpecialLightToggleSignal(rawDataString)) {
            ledState = !ledState; // 切换LED状态
            analogWrite(LED_PIN, ledState ? lightBrightness : 0);
            Serial.println(ledState ? "检测到特定信号，LED已打开" : "检测到特定信号，LED已关闭");
          }
        } else {
          Serial.println("RAW数据包含无效持续时间，未发送。");
        }
      } else {
        Serial.println("RAW数据解析失败或为空");
      }
    } else {
      Serial.println("未知红外协议");
    }
  } else if (doc["command"] == "ir_save") {
    // 接收并保存红外信号
    Serial.println("收到保存红外信号命令");
    saveIrSignalFromMQTT(doc);
  }
}

// 连接MQTT服务器
void connectMQTT() {
  while (!client.connected() && WiFi.status() == WL_CONNECTED) {
    Serial.println("尝试连接MQTT服务器...");
    
    if (client.connect(deviceId.c_str())) {
      Serial.println("MQTT连接成功");
      client.subscribe(controlTopic.c_str());
    } else {
      Serial.print("MQTT连接失败，错误码: ");
      Serial.println(client.state());
      delay(2000);
    }
  }
}

// 发送状态信息
void sendStatus() {
  if (!client.connected()) {
    return;
  }
  
  StaticJsonDocument<400> doc;
  doc["command"] = "status";
  doc["params"]["Id"] = deviceId;
  
  // LED状态
  doc["params"]["led_status"] = ledState ? "on" : "off";
  
  // 传感器数据
  JsonObject data = doc["params"].createNestedObject("data");
  
  // 光照强度
  int ldrValue = analogRead(LDR_AO_PIN);
  float lightIntensity = 100.0 * (4000.0 - ldrValue) / 4000.0;
  lightIntensity = constrain(lightIntensity, 0, 100);
  data["light"] = lightIntensity;
  
  // 温湿度
  float temp = dht.readTemperature();
  float hum = dht.readHumidity();
  if (!isnan(temp) && !isnan(hum)) {
    data["temperature"] = temp;
    data["humidity"] = hum;
  }
  
  // 人体移动
  data["pir"] = digitalRead(pirPin) == HIGH ? "detected" : "none";
  
  // 空气质量
  int mq135Value = analogRead(mq135AnalogPin);
  float gasConcentration = map(mq135Value, 0, 4095, 10, 1000);
  float airQuality = 100.0 * (1000.0 - gasConcentration) / 990.0;
  airQuality = constrain(airQuality, 0, 100);
  data["air_quality"] = airQuality;

  // BMP280数据
  float pressure = bme.readPressure();
  float bmpTemp = bme.readTemperature();
  data["pressure"] = pressure;
  data["bmp_temperature"] = bmpTemp;
  
  String jsonString;
  serializeJson(doc, jsonString);
  client.publish(statusTopic.c_str(), jsonString.c_str());
}

// 初始化MQTT
void initMQTT() {
  deviceId = String((uint32_t)ESP.getEfuseMac(), HEX); // 使用MAC地址作为设备ID
  statusTopic = "device/" + deviceId + "/status";
  controlTopic = "device/" + deviceId + "/control";
  irSignalTopic = "device/" + deviceId + "/ir_signal"; // 初始化红外信号主题
  
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);

  Serial.print("MQTT Status Topic: ");
  Serial.println(statusTopic);
  Serial.print("MQTT Control Topic: ");
  Serial.println(controlTopic);
  Serial.print("MQTT IR Signal Topic: "); // 新增打印红外信号主题
  Serial.println(irSignalTopic);
}

void setup() {
  // 初始化串口通讯
  Serial.begin(9600);
  while (!Serial); // 等待串口连接（仅用于原生USB设备）
  
  // 初始化EEPROM
  EEPROM.begin(EEPROM_SIZE);
  Serial.println("EEPROM已初始化");

  // 设置数字引脚为输入模式
  pinMode(pirPin, INPUT);  // 配置PIR引脚为输入模式

  // 设置按钮引脚为输入，并启用内部上拉电阻
  pinMode(BUTTON_PIN_1, INPUT_PULLUP);
  pinMode(BUTTON_PIN_2, INPUT_PULLUP);
  pinMode(BUTTON_PIN_3, INPUT_PULLUP);
  
  // 设置LED引脚为输出
  pinMode(LED_PIN, OUTPUT);
  analogWrite(LED_PIN, ledState ? lightBrightness : 0); // 设置LED初始状态

  // 初始化 DHT11 传感器
  dht.begin();

  // 初始化 BMP280 传感器
  if (!bme.begin(0x76)) {
    Serial.println("未找到有效的BMP280传感器，请检查接线！");
    while (1);
  }

  // 初始化红外接收和发送
  IrReceiver.begin(IR_RX_PIN);
  IrSender.begin(IR_TX_PIN);

  Serial.println("环境监测系统启动...");
  Serial.println(F("按下遥控器按键学习，输入's'重发"));
  
  // 从Flash加载所有红外信号
  loadAllIrSignals();
  
  // 初始化WiFi连接
  initWiFi();
  
  // 初始化MQTT
  initMQTT();
  

}

void loop() {
  currentTime = millis(); // 添加这行代码
  
  // 如果处于配网模式，不执行其他操作
  if (isConfigMode) {
    delay(10);
    return;
  }
  
  // MQTT连接和消息处理
  if (WiFi.status() == WL_CONNECTED) {
    if (!client.connected()) {
      connectMQTT();
    }
    client.loop();
    
    // 定期发送状态信息
    if (currentTime - lastStatusSend >= statusInterval) {
      lastStatusSend = currentTime;
      sendStatus();
    }
    

  }
  
  // 检查是否到了读取传感器数据的时间
  if (currentTime - lastReadTime >= interval) {
    lastReadTime = currentTime;

    readLDR();
    readDHT();
    // readPIR(); // 移除此行，readPIR将在loop中每次都调用
    readMQ135Sensor(); // 读取MQ-135传感器数据
    readBMP280(); // 读取BMP280传感器数据
  }

  // 每次循环都检测PIR传感器，确保实时响应
  readPIR();

  // 处理红外信号
  if (IrReceiver.decode()) {
    // 将接收到的RAW数据转换为字符串格式，以便传递给checkSpecialLightToggleSignal
    String receivedRawDataString = "";
    for (uint16_t i = 0; i < IrReceiver.decodedIRData.rawDataPtr->rawlen; i++) {
      receivedRawDataString += String(IrReceiver.decodedIRData.rawDataPtr->rawbuf[i] * RAWTICK);
      if (i < IrReceiver.decodedIRData.rawDataPtr->rawlen - 1) {
        receivedRawDataString += ",";
      }
    }

    // 检查接收到的红外信号是否是切换灯状态的特定信号
    int signalType = checkSpecialLightToggleSignal(receivedRawDataString);
    if (signalType == 1) { // 切换灯状态信号
      ledState = !ledState; // 切换LED状态
      analogWrite(LED_PIN, ledState ? lightBrightness : 0);
      Serial.println(ledState ? "接收到特定信号，LED已打开" : "接收到特定信号，LED已关闭");
    } else if (signalType == 2) { // 亮度增加信号
      lightBrightness = min(lightBrightness + 25, 255); // 每次增加25，最大255
      analogWrite(LED_PIN, lightBrightness);
      Serial.print("接收到亮度增加信号，当前亮度: ");
      Serial.println(lightBrightness);
    } else if (signalType == 3) { // 亮度减少信号
      lightBrightness = max(lightBrightness - 25, 0); // 每次减少25，最小0
      analogWrite(LED_PIN, lightBrightness);
      Serial.print("接收到亮度减少信号，当前亮度: ");
      Serial.println(lightBrightness);
    }

    storeIrSignal();
    IrReceiver.resume();
  }

  if (Serial.available() && tolower(Serial.read()) == 's') {
    sendStoredSignal(currentIrSignalIndex);
  }

  // 读取当前按钮状态
  buttonState1 = digitalRead(BUTTON_PIN_1);
  buttonState2 = digitalRead(BUTTON_PIN_2);
  buttonState3 = digitalRead(BUTTON_PIN_3);

  // 检查按钮1是否被按下（从高电平变为低电平），用于发射红外信号
  if (buttonState1 == LOW && lastButtonState1 == HIGH) {
    Serial.println("按钮1被按下 - 检测按压时长...");
    // 记录按下开始时间
    unsigned long pressStartTime = millis();
    bool longPressDetected = false;
    
    // 等待按钮释放或达到长按时间
    while (digitalRead(BUTTON_PIN_1) == LOW) {
      delay(10);
      // 如果按下超过3秒，进入红外信号录入模式
      if (millis() - pressStartTime > 3000) {
        longPressDetected = true;
        
        // 切换录入模式
        isRecordingMode = !isRecordingMode;
        
        if (isRecordingMode) {
          // 进入录入模式，查找最小的空闲索引
          currentIrSignalIndex = findSmallestEmptyIndex();
          
          Serial.print("进入红外信号录入模式，当前索引: ");
          Serial.println(currentIrSignalIndex);
          
          // 闪烁LED指示进入录入模式
          for (int i = 0; i < 3; i++) {
            analogWrite(LED_PIN, lightBrightness);
            delay(200);
            analogWrite(LED_PIN, 0);
            delay(200);
          }
          
          // 清除当前索引的信号数据
          irSignals[currentIrSignalIndex].protocol = UNKNOWN;
          irSignals[currentIrSignalIndex].address = 0;
          irSignals[currentIrSignalIndex].command = 0;
          irSignals[currentIrSignalIndex].rawData = 0;
          irSignals[currentIrSignalIndex].rawLen = 0;
        } else {
          // 退出录入模式
          Serial.println("退出红外信号录入模式");
          
          // 闪烁LED指示退出录入模式
          for (int i = 0; i < 2; i++) {
            analogWrite(LED_PIN, lightBrightness);
            delay(500);
            analogWrite(LED_PIN, 0);
            delay(200);
          }
          
          // 切换到下一个索引
          currentIrSignalIndex = (currentIrSignalIndex + 1) % MAX_IR_SIGNALS;
          // 保存当前索引到Flash
          saveCurrentIndexToFlash();
        }
        
        break;
      }
    }
    
    // 如果不是长按，则执行短按操作（发送红外信号）
    if (!longPressDetected) {
      Serial.println("短按检测到，发送存储的红外信号");
      sendStoredSignal(currentIrSignalIndex); // 发送当前索引的红外信号
    }
    
    delay(50); // 消抖延时
  }
  
  // 检查按钮2是否被按下，用于切换LED状态
  if (buttonState2 == LOW && lastButtonState2 == HIGH) {
    ledState = !ledState; // 切换LED状态
    analogWrite(LED_PIN, ledState ? lightBrightness : 0); 
    Serial.println(ledState ? "LED已打开" : "LED已关闭");
    delay(50);
  }
  
  // 检查按钮3是否被按下，用于触发配网或重启设备
  if (buttonState3 == LOW && lastButtonState3 == HIGH) {
    Serial.println("按钮3被按下 - 检测按压时长...");
    // 记录按下开始时间
    unsigned long pressStartTime = millis();
    bool longPressDetected = false;
    
    // 等待按钮释放或达到长按时间
    while (digitalRead(BUTTON_PIN_3) == LOW) {
      delay(10);
      // 如果按下超过3秒，启动配网
      if (millis() - pressStartTime > 3000) {
        Serial.println("长按检测到，启动配网模式...");
        analogWrite(LED_PIN, lightBrightness); // 点亮LED指示配网模式
        startConfigPortal();
        analogWrite(LED_PIN, ledState ? lightBrightness : 0); // 恢复LED状态
        longPressDetected = true;
        break;
      }
    }
    
    // 如果不是长按，则执行短按操作（重启设备）
    if (!longPressDetected) {
      Serial.println("短按检测到，准备重启ESP32...");
      // 闪烁LED指示即将重启
      for (int i = 0; i < 3; i++) {
        analogWrite(LED_PIN, lightBrightness);
        delay(100);
        analogWrite(LED_PIN, 0);
        delay(100);
      }
      Serial.println("ESP32即将重启...");
      delay(500); // 等待串口消息发送完成
      ESP.restart(); // 重启ESP32
    }
    
    delay(50); // 消抖延时
  }

  // 更新上一次的按钮状态
  lastButtonState1 = buttonState1;
  lastButtonState2 = buttonState2;
  lastButtonState3 = buttonState3;

  delay(10); // 短暂延迟以避免过度占用CPU
}

// 打印信号信息
void printSignalInfo(IrSignal* signal) {
  // 如果未指定信号，使用最近一次存储的信号
  if (signal == nullptr) {
    signal = &lastSignal;
  }
  
  Serial.print(F("协议: "));
  // 使用IRremote库中的getProtocolString函数
  Serial.println(getProtocolString(signal->protocol));

  if (signal->protocol != UNKNOWN) {
    Serial.print(F("地址: 0x"));
    Serial.print(signal->address, HEX);
    Serial.print(F(", 命令: 0x"));
    Serial.println(signal->command, HEX);
  }

  Serial.print(F("原始数据长度: "));
  Serial.println(signal->rawLen);
  
  Serial.println(F("原始数据 (脉冲/间隔): "));
  for (uint16_t i = 0; i < signal->rawLen && i < 200; i++) {
    Serial.print(signal->rawBuf[i]);
    if (i < signal->rawLen - 1) {
      Serial.print(F(","));
    }
  }
  Serial.println();
}

void readLDR() {
  // 读取光敏电阻的模拟值
  int analogValue = analogRead(LDR_AO_PIN);


  // 计算光照强度百分比（模拟值越低，光照强度越强）
  // 假设模拟值范围为0-4000
  float lightIntensity = 100.0 * (4000.0 - analogValue) / 4000.0;
  
  // 确保百分比在0-100范围内
  lightIntensity = constrain(lightIntensity, 0, 100);

  // 打印模拟值、数字值和光照强度到串口监视器
  Serial.print("光敏电阻 - 模拟量: ");
  Serial.print(analogValue);
  Serial.print(" | 光照强度: ");
  Serial.print(lightIntensity, 1); // 保留1位小数
  Serial.println("%");
}

void readDHT() {
  // 每 2 秒读取一次数据（DHT11 推荐的最小读取间隔是 1 秒）
  // 注意：在loop中调用时，需要考虑整体延迟，这里函数内部不加延迟

  // 读取温度（单位：摄氏度）
  float temperature = dht.readTemperature();
  // 读取湿度（单位：百分比）
  float humidity = dht.readHumidity();

  // 检查是否读取失败
  if (isnan(temperature) || isnan(humidity)) {
    Serial.println("温湿度传感器读取失败！请检查传感器连接。");
    return;
  }

  // 打印温度和湿度数据到串口监视器
  Serial.print("温湿度传感器 - 温度: ");
  Serial.print(temperature);
  Serial.print(" °C");

  Serial.print("  湿度: ");
  Serial.print(humidity);
  Serial.println(" %");
}

int lastPirState = LOW; // 添加一个变量来存储上一次的PIR状态

unsigned long lastPirTriggerTime = 0; // 上次PIR触发时间
const long pirReportInterval = 10000; // PIR上报间隔，10秒

void readPIR() {
  int pirValue = digitalRead(pirPin);

  // 检查当前状态是否与上次状态不同
  if (pirValue != lastPirState) {
    // 如果状态改变，更新lastPirState并打印信息
    lastPirState = pirValue;

    if (pirValue == HIGH) {
      Serial.println("人体移动传感器 - 检测到人体移动！");
      // 只有当距离上次上报时间超过pirReportInterval时才上报
      if (millis() - lastPirTriggerTime >= pirReportInterval) {
        if (WiFi.status() == WL_CONNECTED && client.connected()) {
          // 读取所有传感器数据
          readLDR();
          readDHT();
          readMQ135Sensor();
          readBMP280();
          // 立即发送状态信息到MQTT
          sendStatus();
          Serial.println("检测到人体移动，已上报MQTT数据");
          lastPirTriggerTime = millis(); // 更新上次上报时间
        }
      } else {
        Serial.println("人体移动传感器 - 移动检测，但未到上报间隔。");
      }
    } else {
      Serial.println("人体移动传感器 - 移动停止。");
    }
  }
  // 延迟在loop中统一处理，这里不加
}

void readMQ135Sensor() {
  // 读取模拟值
  int analogValue = analogRead(mq135AnalogPin);
  // 读取数字值
  
  // 将模拟值转换为空气质量百分比
  // 假设模拟值范围为0-4095（ESP32的ADC分辨率），对应气体浓度10-1000ppm
  // 值越低，空气质量越好
  float gasConcentration = map(analogValue, 0, 4095, 10, 1000);
  
  // 计算空气质量百分比（反向映射，浓度越低，质量越高）
  // 10ppm对应100%，1000ppm对应0%
  float airQualityPercentage = 100.0 * (1000.0 - gasConcentration) / 990.0;
  
  // 确保百分比在0-100范围内
  airQualityPercentage = constrain(airQualityPercentage, 0, 100);
  
  // 打印到串口监视器
  Serial.print("MQ-135传感器 - 空气质量: ");
  Serial.print(airQualityPercentage, 1); // 打印空气质量百分比，保留1位小数
  Serial.print("%");
  Serial.print(" | 模拟量 (AO): ");
  Serial.print(analogValue); // 打印原始模拟量
  Serial.println(); // 添加换行
}

void readBMP280() {
  // 读取BMP280数据
  float pressure = bme.readPressure();
  float bmpTemp = bme.readTemperature();

  // 检查是否读取失败
  if (isnan(pressure) || isnan(bmpTemp)) {
    Serial.println("BMP280传感器读取失败！");
    return;
  }

  // 打印到串口监视器
  Serial.print("BMP280传感器 - 压力: ");
  Serial.print(pressure / 100.0F); // 转换为hPa
  Serial.print(" hPa");

  Serial.print(" | 温度: ");
  Serial.print(bmpTemp);
  Serial.println(" °C");
}

// 这些变量已移至文件顶部

// 查找最小的空闲索引
int findSmallestEmptyIndex() {
  for (int i = 0; i < MAX_IR_SIGNALS; i++) {
    if (irSignals[i].rawLen <= 20) {
      return i; // 找到一个空闲索引
    }
  }
  return 0; // 如果没有空闲索引，则返回0
}

// 存储红外信号
void storeIrSignal() {
  // 检查是否为重发信号
  if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
    Serial.println(F("检测到重发信号，不存储"));
    return;
  }
  
  // 检查信号长度
  if (IrReceiver.decodedIRData.rawDataPtr->rawlen < 20) {
    Serial.println(F("信号太短，不存储"));
    return;
  }

  // 检查信号长度是否超过限制
  if (IrReceiver.decodedIRData.rawDataPtr->rawlen > 500) {
    Serial.print(F("警告：信号长度("));
    Serial.print(IrReceiver.decodedIRData.rawDataPtr->rawlen);
    Serial.print(F(")超过限制(500)，将被截断"));
  }

  // 存储到最近一次信号变量中
  lastSignal.protocol = IrReceiver.decodedIRData.protocol;
  lastSignal.address = IrReceiver.decodedIRData.address;
  lastSignal.command = IrReceiver.decodedIRData.command;
  lastSignal.rawData = IrReceiver.decodedIRData.decodedRawData;
  
  // 打印原始接收数据的详细信息（调试用）
  Serial.println(F("接收到的原始数据详情："));
  Serial.print(F("rawlen = "));
  Serial.println(IrReceiver.decodedIRData.rawDataPtr->rawlen);
  
  // 存储RAW数据
  lastSignal.rawLen = IrReceiver.decodedIRData.rawDataPtr->rawlen;
  for (uint16_t i = 0; i < lastSignal.rawLen && i < 200; i++) {
    lastSignal.rawBuf[i] = IrReceiver.decodedIRData.rawDataPtr->rawbuf[i] * RAWTICK;
    
    // 打印每个原始数据点（调试用）
    if (i < 10) { // 只打印前10个数据点，避免输出过多
      Serial.print(F("rawbuf["));
      Serial.print(i);
      Serial.print(F("] = "));
      Serial.print(IrReceiver.decodedIRData.rawDataPtr->rawbuf[i]);
      Serial.print(F(" * RAWTICK = "));
      Serial.println(lastSignal.rawBuf[i]);
    }
  }

  // 如果处于录入模式，则存储到当前索引
  if (isRecordingMode) {
    // 如果当前索引已有信号，则查找最小的空闲索引
    if (irSignals[currentIrSignalIndex].rawLen > 20) {
      int emptyIndex = findSmallestEmptyIndex();
      if (emptyIndex != currentIrSignalIndex) {
        currentIrSignalIndex = emptyIndex;
        Serial.print(F("当前索引已有信号，自动切换到空闲索引: "));
        Serial.println(currentIrSignalIndex);
      }
    }
    
    irSignals[currentIrSignalIndex].protocol = lastSignal.protocol;
    irSignals[currentIrSignalIndex].address = lastSignal.address;
    irSignals[currentIrSignalIndex].command = lastSignal.command;
    irSignals[currentIrSignalIndex].rawData = lastSignal.rawData;
    
    // 复制RAW数据
    irSignals[currentIrSignalIndex].rawLen = lastSignal.rawLen;
    for (uint16_t i = 0; i < lastSignal.rawLen && i < 500; i++) {
      irSignals[currentIrSignalIndex].rawBuf[i] = lastSignal.rawBuf[i];
    }
    
    Serial.print(F("信号已存储到索引 "));
    Serial.println(currentIrSignalIndex);
    
    // 将信号保存到Flash
    saveIrSignalToFlash(currentIrSignalIndex);
  }

  Serial.println(F("--- 信号已存储 ---"));
  printSignalInfo(&lastSignal);

  // 将红外信号数据以JSON格式发布到MQTT
  StaticJsonDocument<512> doc;
  // 使用IRremote库中的getProtocolString函数
  doc["protocol"] = getProtocolString(lastSignal.protocol);
  doc["address"] = lastSignal.address;
  doc["command"] = lastSignal.command;
  doc["rawData"] = lastSignal.rawData;
  doc["index"] = currentIrSignalIndex; // 添加索引信息

  // 对于RAW协议，添加原始脉冲/间隔数据
  if (lastSignal.protocol == UNKNOWN) {
    JsonArray rawDataArray = doc.createNestedArray("raw_pulses");
    for (uint16_t i = 0; i < lastSignal.rawLen && i < 200; i++) {
      rawDataArray.add(lastSignal.rawBuf[i]);
    }
    doc["raw_len"] = lastSignal.rawLen;
  }

  String jsonString;
  serializeJson(doc, jsonString);
  client.publish(irSignalTopic.c_str(), jsonString.c_str());
  Serial.println("红外信号已发布到MQTT");
}

// 发送存储的信号
void sendStoredSignal(int index) {
  // 如果未指定索引或索引无效，使用最近一次存储的信号
  IrSignal* signalToSend = &lastSignal;
  
  if (index >= 0 && index < MAX_IR_SIGNALS) {
    // 检查指定索引的信号是否有效
    if (irSignals[index].rawLen > 20) {
      signalToSend = &irSignals[index];
      Serial.print(F("\n--- 发送索引 "));
      Serial.print(index);
      Serial.println(F(" 的信号 ---"));
    } else {
      Serial.print(F("\n--- 索引 "));
      Serial.print(index);
      Serial.println(F(" 没有有效信号，使用最近一次存储的信号 ---"));
    }
  } else {
    Serial.println(F("\n--- 发送最近一次存储的信号 ---"));
  }
  
  // 打印信号信息
  printSignalInfo(signalToSend);
  
  // 如果是UNKNOWN协议且原始数据长度小于等于20，则不发送
  if (signalToSend->rawLen <= 20) {
    Serial.println(F("信号太短，不发送"));
    return;
  }

  switch (signalToSend->protocol) {
    case NEC:
      IrSender.sendNEC(signalToSend->address, signalToSend->command, 0);
      break;
    case SONY:
      IrSender.sendSony(signalToSend->address, signalToSend->command, 0);  // 不重复发送
      break;
    case RC5:
    case RC6:
      IrSender.sendRC5(signalToSend->address, signalToSend->command, 0);
      break;
    default:
      // 使用通用RAW发送
      if (signalToSend->rawLen > 0) {
        // 将存储的RAW数据转换为uint16_t数组
        uint16_t rawData[500];
        for (uint16_t i = 0; i < signalToSend->rawLen && i < 500; i++) {
          rawData[i] = signalToSend->rawBuf[i];
        }
        
        // 打印调试信息
        Serial.print(F("发送RAW数据，数据长度: "));
        Serial.println(signalToSend->rawLen);
        
        // 发送RAW数据
        IrSender.sendRaw(rawData, signalToSend->rawLen, 38);  // 38kHz载波
      }
  }
  Serial.println(F("信号已发送！"));
}



// 检查是否是特定的切换灯光状态的信号
// 返回值：0-不匹配，1-切换灯状态，2-亮度增加，3-亮度减少
int checkSpecialLightToggleSignal(String rawDataString) {
  // 特定信号的原始数据
  const int toggleSignal[] = {0,1250,450,1250,400,500,1350,1200,500,1250,450,1300,500,1200,500,400,1350,400,1300,400,1350,400,1350,400};
  const int toggleSignalLength = sizeof(toggleSignal) / sizeof(toggleSignal[0]);

  const int brightnessUpSignal[] = {0,1300,500,1250,450,400,1350,1250,500,350,1350,1300,450,400,1350,400,1300,400,1350,400,1350,350,1350,400};
  const int brightnessUpSignalLength = sizeof(brightnessUpSignal) / sizeof(brightnessUpSignal[0]);

  const int brightnessDownSignal[] = {0,1300,450,1300,450,400,1350,1300,400,1300,450,400,1350,400,1300,400,1350,400,1350,400,1350,350,1350,400};
  const int brightnessDownSignalLength = sizeof(brightnessDownSignal) / sizeof(brightnessDownSignal[0]);
  // const int specialSignalLength = sizeof(specialSignal) / sizeof(specialSignal[0]); // 原始切换信号的长度，现在由toggleSignalLength代替
  const int errorRange = 200; // 误差允许范围
  
  // 解析接收到的信号字符串为数组
  String currentValue = "";
  int receivedSignal[500]; // 假设最大长度为500，以适应更长的红外信号
  int receivedLength = 0;
  
  // 解析字符串为数组
  for (int i = 0; i < rawDataString.length(); i++) {
    if (rawDataString[i] == ',') {
      if (receivedLength < 500) { // Add bounds check
        receivedSignal[receivedLength++] = currentValue.toInt();
      } else {
        Serial.println("Error: receivedSignal buffer overflow during parsing!");
        return 0; // Or handle error appropriately
      }
      currentValue = "";
    } else {
      currentValue += rawDataString[i];
    }
  }
  // 添加最后一个数值
  if (currentValue.length() > 0) {
    if (receivedLength < 500) { // Add bounds check for the last value
      receivedSignal[receivedLength++] = currentValue.toInt();
    } else {
      Serial.println("Error: receivedSignal buffer overflow for last value!");
      return 0; // Or handle error appropriately
    }
  }
  
  // 检查是否匹配切换灯状态信号
  if (compareRawSignals(receivedSignal, receivedLength, toggleSignal, toggleSignalLength, errorRange)) {
    return 1;
  }

  // 检查是否匹配亮度增加信号
  if (compareRawSignals(receivedSignal, receivedLength, brightnessUpSignal, brightnessUpSignalLength, errorRange)) {
    return 2;
  }

  // 检查是否匹配亮度减少信号
  if (compareRawSignals(receivedSignal, receivedLength, brightnessDownSignal, brightnessDownSignalLength, errorRange)) {
    return 3;
  }

  return 0; // 不匹配任何特定信号
}

// 辅助函数，用于比较两个RAW数据数组是否在误差范围内
bool compareRawSignals(const int* signal1, int len1, const int* signal2, int len2, int errorRange) {
  if (len1 != len2) {
    return false;
  }
  for (int i = 0; i < len1; i++) {
    if (abs(signal1[i] - signal2[i]) > errorRange) {
      return false;
    }
  }
  return true;
}

// IRremote库已提供getProtocolString函数，不需要自定义

// 保存红外信号到EEPROM
void saveIrSignalToFlash(int index) {
  if (index < 0 || index >= MAX_IR_SIGNALS) {
    Serial.println("无效的信号索引，无法保存");
    return;
  }
  
  // 计算存储地址
  int addr = IR_SIGNALS_START_ADDR + index * sizeof(IrSignal);
  
  // 直接写入整个结构体到EEPROM
  EEPROM.put(addr, irSignals[index]);
  EEPROM.commit();
  
  Serial.print("红外信号已保存到EEPROM，索引: ");
  Serial.print(index);
  Serial.print(", 地址: ");
  Serial.print(addr);
  Serial.print(", 长度: ");
  Serial.println(irSignals[index].rawLen);
}

// 从EEPROM加载红外信号
void loadIrSignalFromFlash(int index) {
  if (index < 0 || index >= MAX_IR_SIGNALS) {
    Serial.println("无效的信号索引，无法加载");
    return;
  }
  
  // 计算存储地址
  int addr = IR_SIGNALS_START_ADDR + index * sizeof(IrSignal);
  
  // 先初始化信号数组
  irSignals[index].protocol = UNKNOWN;
  irSignals[index].address = 0;
  irSignals[index].command = 0;
  irSignals[index].rawData = 0;
  irSignals[index].rawLen = 0;
  for (int i = 0; i < 500; i++) {
    irSignals[index].rawBuf[i] = 0;
  }
  
  // 直接从EEPROM读取整个结构体
  EEPROM.get(addr, irSignals[index]);
  
  // 验证数据有效性
  if (irSignals[index].rawLen > 500) {
    Serial.print("EEPROM中索引 ");
    Serial.print(index);
    Serial.println(" 的数据无效，重置为空");
    irSignals[index].protocol = UNKNOWN;
    irSignals[index].address = 0;
    irSignals[index].command = 0;
    irSignals[index].rawData = 0;
    irSignals[index].rawLen = 0;
    return;
  }
  
  if (irSignals[index].rawLen == 0) {
    Serial.print("EEPROM中不存在索引 ");
    Serial.print(index);
    Serial.println(" 的红外信号");
    return;
  }
  
  Serial.print("已从EEPROM加载红外信号，索引: ");
  Serial.print(index);
  Serial.print(", 地址: ");
  Serial.print(addr);
  Serial.print(", 长度: ");
  Serial.print(irSignals[index].rawLen);
  if (irSignals[index].rawLen > 0) {
    Serial.print(", 前5个数据: ");
    for (int i = 0; i < min(5, (int)irSignals[index].rawLen); i++) {
      Serial.print(irSignals[index].rawBuf[i]);
      if (i < 4 && i < irSignals[index].rawLen - 1) Serial.print(",");
    }
  }
  Serial.println();
}

// 删除EEPROM中的红外信号
void deleteIrSignalFromFlash(int index) {
  if (index < 0 || index >= MAX_IR_SIGNALS) {
    Serial.println("无效的信号索引，无法删除");
    return;
  }
  
  // 清除内存中的信号数据
  irSignals[index].protocol = UNKNOWN;
  irSignals[index].address = 0;
  irSignals[index].command = 0;
  irSignals[index].rawData = 0;
  irSignals[index].rawLen = 0;
  for (int i = 0; i < 500; i++) {
    irSignals[index].rawBuf[i] = 0;
  }
  
  // 计算存储地址并写入清空的数据
  int addr = IR_SIGNALS_START_ADDR + index * sizeof(IrSignal);
  EEPROM.put(addr, irSignals[index]);
  EEPROM.commit();
  
  Serial.print("已删除EEPROM中的红外信号，索引: ");
  Serial.println(index);
}

// 发送Flash存储的红外信号到MQTT
void sendIrSignalInfoToMQTT(int index) {
  if (!client.connected()) {
    return;
  }
  
  if (index < 0 || index >= MAX_IR_SIGNALS) {
    Serial.println("无效的信号索引，无法发送");
    return;
  }
  
  // 检查信号是否有效
  if (irSignals[index].rawLen <= 20) {
    Serial.println("信号无效或太短，无法发送");
    return;
  }
  
  StaticJsonDocument<2048> doc;
  doc["command"] = "ir_signal_data";
  doc["device_id"] = deviceId;
  doc["index"] = index;
  doc["protocol"] = getProtocolString(irSignals[index].protocol);
  doc["address"] = irSignals[index].address;
  doc["command"] = irSignals[index].command;
  doc["raw_len"] = irSignals[index].rawLen;
  
  // 对于RAW协议，添加完整原始数据
  if (irSignals[index].protocol == UNKNOWN) {
    JsonArray rawData = doc.createNestedArray("raw_data");
    for (uint16_t i = 0; i < irSignals[index].rawLen && i < 500; i++) {
      rawData.add(irSignals[index].rawBuf[i]);
    }
  }
  
  String jsonString;
  serializeJson(doc, jsonString);
  client.publish(irSignalTopic.c_str(), jsonString.c_str());
  Serial.print("红外信号 ");
  Serial.print(index);
  Serial.println(" 的数据已发布到MQTT");
}

// 从MQTT接收的数据中保存红外信号
void saveIrSignalFromMQTT(JsonDocument& doc) {
  // 检查必要的参数
  if (!doc.containsKey("protocol") || !doc.containsKey("raw_data")) {
    Serial.println("接收到的红外信号数据不完整，无法保存");
    return;
  }
  
  int index;
  // 如果指定了索引，则使用指定的索引
  if (doc.containsKey("index")) {
    index = doc["index"];
    if (index < 0 || index >= MAX_IR_SIGNALS) {
      Serial.println("无效的信号索引，将自动选择空闲索引");
      index = findSmallestEmptyIndex();
    } else if (irSignals[index].rawLen > 20) {
      // 如果指定的索引已有信号，询问是否覆盖
      if (doc.containsKey("force_overwrite") && doc["force_overwrite"].as<bool>()) {
        // 强制覆盖
        Serial.print("将覆盖索引 ");
        Serial.print(index);
        Serial.println(" 的现有信号");
      } else {
        Serial.print("索引 ");
        Serial.print(index);
        Serial.println(" 已有信号，将自动选择空闲索引");
        index = findSmallestEmptyIndex();
      }
    }
  } else {
    // 如果未指定索引，则自动选择最小的空闲索引
    index = findSmallestEmptyIndex();
  }
  
  Serial.print("将保存信号到索引: ");
  Serial.println(index);
  
  // 将协议名称转换为decode_type_t
  String protocolName = doc["protocol"];
  decode_type_t protocol = UNKNOWN;
  
  // 尝试匹配协议名称
  if (protocolName == "NEC") {
    protocol = NEC;
  } else if (protocolName == "SONY") {
    protocol = SONY;
  } else if (protocolName == "RC5") {
    protocol = RC5;
  } else if (protocolName == "RC6") {
    protocol = RC6;
  } else {
    protocol = UNKNOWN;
  }
  
  // 保存基本信息
  irSignals[index].protocol = protocol;
  irSignals[index].address = doc.containsKey("address") ? doc["address"] : 0;
  irSignals[index].command = doc.containsKey("command") ? doc["command"] : 0;
  irSignals[index].rawData = doc.containsKey("rawData") ? doc["rawData"] : 0;
  
  // 保存RAW数据
  JsonArray rawData = doc["raw_data"];
  irSignals[index].rawLen = rawData.size();
  
  if (irSignals[index].rawLen > 500) {
    Serial.print("警告：信号长度(");
    Serial.print(irSignals[index].rawLen);
    Serial.println(")超过限制(500)，将被截断");
    irSignals[index].rawLen = 500; // 限制最大长度
  }
  
  for (uint16_t i = 0; i < irSignals[index].rawLen; i++) {
    irSignals[index].rawBuf[i] = rawData[i];
  }
  
  // 保存到Flash
  saveIrSignalToFlash(index);
  
  // 更新当前索引
  currentIrSignalIndex = index;
  saveCurrentIndexToFlash();
  
  Serial.print("已从MQTT接收并保存红外信号到索引 ");
  Serial.println(index);
  
  // 打印信号信息
  printSignalInfo(&irSignals[index]);
  
  // 发送确认消息回MQTT
  if (client.connected()) {
    StaticJsonDocument<512> response;
    response["command"] = "ir_save_result";
    response["status"] = "success";
    response["index"] = index;
    response["message"] = "信号已成功保存";
    
    String jsonString;
    serializeJson(response, jsonString);
    client.publish(irSignalTopic.c_str(), jsonString.c_str());
  }
}

// 保存当前索引值到EEPROM
void saveCurrentIndexToFlash() {
  EEPROM.put(CURRENT_INDEX_ADDR, currentIrSignalIndex);
  EEPROM.commit();
  
  Serial.print("当前索引已保存到EEPROM: ");
  Serial.println(currentIrSignalIndex);
}

// 从EEPROM加载当前索引值
void loadCurrentIndexFromFlash() {
  EEPROM.get(CURRENT_INDEX_ADDR, currentIrSignalIndex);
  
  // 验证索引有效性
  if (currentIrSignalIndex >= MAX_IR_SIGNALS) {
    currentIrSignalIndex = 0;
  }
  
  Serial.print("从EEPROM加载当前索引: ");
  Serial.println(currentIrSignalIndex);
}

// 加载所有红外信号
void loadAllIrSignals() {
  Serial.println("正在从EEPROM加载所有红外信号...");
  
  // 先加载当前索引
  loadCurrentIndexFromFlash();
  
  // 然后加载所有信号
  for (int i = 0; i < MAX_IR_SIGNALS; i++) {
    loadIrSignalFromFlash(i);
  }
  Serial.println("红外信号加载完成");
}